package com.harveyspace.aes.social.facebook;

import com.harveyspace.aes.core.utils.JsonUtil;
import com.harveyspace.aes.social.Oauth;
import com.harveyspace.aes.social.SocialConfig;
import com.harveyspace.aes.social.exception.SocialException;
import com.harveyspace.aes.social.pojo.AccessTokenInfo;
import com.harveyspace.aes.social.pojo.SocialiteUserInfo;
import com.harveyspace.aes.social.utils.HttpClientSSL;
import java.util.HashMap;
import java.util.Map;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;

/**
 * facebook 登录
 * <p>
 * okgr.vpgame.com graph.facebook.com
 *
 * @Author <a href="harveyer@163.com">harvey</a>
 * @date 2018/2/9
 */
public class OauthFacebook extends Oauth {

    private final String FACEBOOK_AUTH_URL = "https://www.facebook.com/%s/dialog/oauth";
    private final String TOKEN_URL = "https://okgr.vpgame.com/%s/oauth/access_token";
    private final String USER_INFO_URL = "https://okgr.vpgame.com/%s/me";
    private final String USER_PICTURE_URL = "https://okgr.vpgame.com/%s/%s/picture?type=normal";
    private final String VERSION = "v2.5";

    private FacebookUserInfo facebookUserInfo;

    /**
     * @param socialConfig 配置信息
     */
    public OauthFacebook(SocialConfig socialConfig) {
        super(socialConfig);
        AUTH_URL = String.format(FACEBOOK_AUTH_URL, VERSION);
    }

    /**
     * 获取授权url
     *
     * @param state OAuth2.0标准协议建议，利用state参数来防止CSRF攻击
     * @return String
     */
    @Override
    public String getAuthorizeUrl(String state) {
        return super.getAuthorizeUrl(state);
    }

    /**
     * 获取用户信息
     *
     * @param accessToken AccessToken
     * @return String 返回类型
     * @throws SocialException 获取用户信息异常
     */
    private FacebookUserInfo getUserInfo(String accessToken) throws SocialException {

        Map<String, String> params = new HashMap<>();
        params.put("access_token", accessToken);
        params.put("appsecret_proof", generateAppSecretProof(accessToken));
        params.put("fileds", "id, first_name, last_name, email, gender");

        String result = "";
        Map<String, Object> responseMap;
        try {

            String userInfoUrl = initParams(String.format(USER_INFO_URL, VERSION), params);
            result = HttpClientSSL.getString(userInfoUrl);

            responseMap = JsonUtil.toMap(result);
        } catch (Exception e) {
            String errorMessage = String.format("通过 accessToken[%s] 获取用户信息异常：%s", accessToken, result);
            logger.error(errorMessage, e);
            throw new SocialException(errorMessage);
        }

        if (StringUtils.isNotBlank(MapUtils.getString(responseMap, "error"))) {

            String errorMessage = MapUtils.getString(responseMap, "error");

            throw new SocialException(String.format("通过 accessToken[%s] 获取用户信息异常：%s", accessToken, errorMessage));
        }

        return JsonUtil.toObject(result, FacebookUserInfo.class);
    }

    /**
     * 获取token 返回的是JSON 解析获取到Token以及OpenId
     *
     * @param code 使用code换取token
     * @return String 返回类型
     * @throws SocialException 获取access token 异常
     */
    @Override
    public AccessTokenInfo getAccessToken(String code) throws SocialException {

        Map<String, String> params = new HashMap<>();
        params.put("code", code);
        params.put("client_id", socialConfig.getClientId());
        params.put("client_secret", socialConfig.getClientSecret());
        params.put("redirect_uri", socialConfig.getRedirectUri());

        String result = "";
        Map<String, Object> responseMap;
        try {
            String verifyTokenUrl = initParams(String.format(TOKEN_URL, VERSION), params);
            result = HttpClientSSL.getString(verifyTokenUrl);

            responseMap = JsonUtil.toMap(result);
        } catch (Exception e) {

            String errorMessage = String.format("通过 code[%s] 获取 access token异常:%s", code, result);
            logger.error(errorMessage, e);
            throw new SocialException(errorMessage);
        }

        if (StringUtils.isNotBlank(MapUtils.getString(responseMap, "error"))) {

            String errorMessage = MapUtils.getString(responseMap, "error");

            throw new SocialException(String.format("通过 code[%s] 获取 access token异常:%s", code, errorMessage));
        }

        AccessTokenInfo accessTokenInfo = JsonUtil.toObject(result, AccessTokenInfo.class);

        facebookUserInfo = getUserInfo(accessTokenInfo.getAccessToken());
        facebookUserInfo.setAvatar(String.format(USER_PICTURE_URL, VERSION, facebookUserInfo.getId()));

        accessTokenInfo.setOpenId(facebookUserInfo.getId());
        accessTokenInfo.setUnionId(facebookUserInfo.getId());

        return accessTokenInfo;
    }

    @Override
    public SocialiteUserInfo getSocialUserInfo(AccessTokenInfo accessTokenInfo) {

        SocialiteUserInfo socialiteUserInfo = new SocialiteUserInfo();
        socialiteUserInfo.setUnionId(accessTokenInfo.getUnionId());
        socialiteUserInfo.setOpenId(accessTokenInfo.getOpenId());

        if (null != facebookUserInfo) {
            String name = facebookUserInfo.getName();

            socialiteUserInfo.setAvatar(facebookUserInfo.getAvatar());
            socialiteUserInfo.setNickName(name);
            socialiteUserInfo.setName(name);
        }

        return socialiteUserInfo;
    }

    /**
     * 通过 sha256，对access_token，client_secret加密生成的签名验证码
     *
     * @param accessToken 对access_token
     * @return 加密生成的签名验证码
     * @throws SocialException 签名验证码生成异常
     */
    private String generateAppSecretProof(String accessToken) throws SocialException {
        String secret = socialConfig.getClientSecret();

        try {

            Mac hmac = Mac.getInstance("HmacSHA256");
            SecretKeySpec secretKey = new SecretKeySpec(secret.getBytes("UTF-8"), "HmacSHA256");
            hmac.init(secretKey);

            byte[] signature = hmac.doFinal(accessToken.getBytes("UTF-8"));

            return Hex.encodeHexString(signature);

        } catch (Exception e) {

            String errorMessage = String.format("生成 signature [%s]异常", accessToken);
            logger.error(errorMessage, e);
            throw new SocialException(errorMessage);
        }
    }
}
